<!DOCTYPE html>
<html  lang="zh-CN" >
<head>
  <meta charset="UTF-8">
  <meta name="viewport"
    content="width=device-width, initial-scale=1.0, minimum-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <meta name="theme-color" content="#fff" id="theme-color">
  <meta name="description" content="深山磨柴斧，一鸣震江湖">
  <link rel="icon" href="/img/ic_head.jpg">
  <title>009-Android自定义View(4)：自绘式详解(图文)</title>
  
  
  <meta property="og:title" content="009-Android自定义View(4)：自绘式详解(图文)">
  
  
  <meta property="og:url" content="http://example.com/2019/06/29/009-Android%E8%87%AA%E5%AE%9A%E4%B9%89View(4)%EF%BC%9A%E8%87%AA%E7%BB%98%E5%BC%8F%E8%AF%A6%E8%A7%A3(%E5%9B%BE%E6%96%87)/index.html">
  
  
  <meta property="og:img" content="/img/ic_head.jpg">
  
  
  <meta property="og:img" content="深山磨柴斧，一鸣震江湖">
  
  
  <meta property="og:type" content="article">
  <meta property="og:article:published_time" content="2019-06-29">
  <meta property="og:article:modified_time" content="2021-04-14">
  <meta property="og:article:author" content="Cupster">
  
  
  <meta property="og:article:tag" content="Android">
  
  <meta property="og:article:tag" content="Android自定义View">
  
  
  
  
  <script>
    // control reverse button
    var reverseDarkList = {
      dark: 'light',
      light: 'dark'
    };
    var themeColor = {
      dark: '#1c1c1e',
      light: '#fff'
    }
    // get the data of css prefers-color-scheme
    var getCssMediaQuery = function() {
      return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
    };
    // reverse current darkmode setting function
    var reverseDarkModeSetting = function() {
      var setting = localStorage.getItem('user-color-scheme');
      if(reverseDarkList[setting]) {
        setting = reverseDarkList[setting];
      } else if(setting === null) {
        setting = reverseDarkList[getCssMediaQuery()];
      } else {
        return;
      }
      localStorage.setItem('user-color-scheme', setting);
      return setting;
    };
    // apply current darkmode setting
    var setDarkmode = function(mode) {
      var setting = mode || localStorage.getItem('user-color-scheme');
      if(setting === getCssMediaQuery()) {
        document.documentElement.removeAttribute('data-user-color-scheme');
        localStorage.removeItem('user-color-scheme');
        document.getElementById('theme-color').content = themeColor[setting];
      } else if(reverseDarkList[setting]) {
        document.documentElement.setAttribute('data-user-color-scheme', setting);
        document.getElementById('theme-color').content = themeColor[setting];
      } else {
        document.documentElement.removeAttribute('data-user-color-scheme');
        localStorage.removeItem('user-color-scheme');
        document.getElementById('theme-color').content = themeColor[getCssMediaQuery()];
      }
    };
    setDarkmode();
  </script>
  <script>
    function loadScript(url, cb) {
      var script = document.createElement('script');
      script.src = url;
      if (cb) script.onload = cb;
      script.async = true;
      document.body.appendChild(script);
    }
  </script>
  
  <link rel="preload" href="//at.alicdn.com/t/font_1946621_i1kgafibvw.css" as="style" >
  <link rel="preload" href="//at.alicdn.com/t/font_1952792_89b4ac4k4up.css" as="style" >
  
  <link rel="preload" href="/js/lib/lightbox/baguetteBox.min.js" as="script">
  <link rel="preload" href="/js/lib/lightbox/baguetteBox.min.css" as="style" >
  
  
  <link rel="preload" href="/js/lib/lozad.min.js" as="script">
  
  
  
  
  
  
  
  
<link rel="stylesheet" href="/css/main.css">

  
  
<link rel="stylesheet" href="//at.alicdn.com/t/font_1946621_i1kgafibvw.css">

  
  
<link rel="stylesheet" href="//at.alicdn.com/t/font_1952792_89b4ac4k4up.css">

  
  
<link rel="stylesheet" href="/js/lib/lightbox/baguetteBox.min.css">

  
  
  
  
<meta name="generator" content="Hexo 5.4.0"></head>


<body>
  <div class="wrapper">
    
    <nav class="navbar">
  <div class="navbar-logo">
    <span class="navbar-logo-main">
      
      <img class="navbar-logo-img" src="/img/ic_head.jpg" alt="logo">
      
      <span class="navbar-logo-dsc">不负明天</span>
    </span>
  </div>
  <div class="navbar-menu">
    
    <a href="/" class="navbar-menu-item">
    
    首页
    
    </a>
    
    <a href="/archives" class="navbar-menu-item">
    
    归档
    
    </a>
    
    <a href="/tags" class="navbar-menu-item">
    
    标签
    
    </a>
    
    <a href="/categories" class="navbar-menu-item">
    
    分类
    
    </a>
    
    <a href="/about" class="navbar-menu-item">
    
    关于
    
    </a>
    
    <a href="/links" class="navbar-menu-item">
    
    友链
    
    </a>
    
    <a class="navbar-menu-item darknavbar" id="dark"><i class="iconfont icon-weather"></i></a>
    <a class="navbar-menu-item searchnavbar" id="search"><i class="iconfont icon-search" style="font-size: 1.2rem; font-weight: 400;"></i></a>
  </div>
</nav>
    
    <div id="local-search" style="display: none;">
      <input class="navbar-menu-item" id="search-input" placeholder="请输入搜索内容...">
      <div id="search-content"></div>
    </div>
    
    <div class="section-wrap">
      <div class="container">
        <div class="columns">
          <main class="main-column">
<article class="card card-content">
  <header>
    <h1 class="post-title">
      009-Android自定义View(4)：自绘式详解(图文)
    </h1>
  </header>
  <div class="post-meta post-show-meta">
    <time datetime="2019-06-29T04:14:22.000Z">
      <i class="iconfont icon-calendar" style="margin-right: 2px;"></i>
      <span>2019-06-29</span>
    </time>
    
    <span class="dot"></span>
    
    <a href="/categories/Android/" class="post-meta-link">Android</a>
    
    
    
    <span class="dot"></span>
    <span>2.7k 字</span>
    
  </div>
  
  <div class="post-meta post-show-meta" style="margin-top: -10px;">
    <div style="display: flex; align-items: center;">
      <i class="iconfont icon-biaoqian" style="margin-right: 2px; font-size: 1.15rem;"></i>
      
      
        <a href="/tags/Android/" class="post-meta-link">Android</a>
      
      
      <span class="dot"></span>
      
        <a href="/tags/Android自定义View/" class="post-meta-link">Android自定义View</a>
      
    </div>
  </div>
  
  </header>
  <div id="section" class="post-content">
    <h1 id="1-简述"><a href="#1-简述" class="headerlink" title="1. 简述"></a>1. 简述</h1><p>建议：熟悉View的绘制、Android事件传递、手势处理再常用自绘式。</p>
<p>方式：直接继承View ，重写绘制流程三步骤</p>
<p>measure() 测量<br>layout() 布局计算摆放坐标<br>draw() 绘制</p>
<h1 id="2-前提基础"><a href="#2-前提基础" class="headerlink" title="2. 前提基础"></a>2. 前提基础</h1><h4 id="2-1了解View关联Activity时的生命周期"><a href="#2-1了解View关联Activity时的生命周期" class="headerlink" title="2.1了解View关联Activity时的生命周期"></a>2.1了解View关联Activity时的生命周期</h4><p>可参考 ： <a target="_blank" rel="noopener" href="https://blog.csdn.net/Cupster/article/details/112055331">Android自定义View(1)：基础</a></p>
<h4 id="2-2-了解自定义View的自定义属性，以及获取、使用"><a href="#2-2-了解自定义View的自定义属性，以及获取、使用" class="headerlink" title="2.2 了解自定义View的自定义属性，以及获取、使用"></a>2.2 了解自定义View的自定义属性，以及获取、使用</h4><p>可参考 ： <a target="_blank" rel="noopener" href="https://blog.csdn.net/Cupster/article/details/112055331">Android自定义View(1)：基础</a></p>
<h4 id="2-3-重要的生命周期函数"><a href="#2-3-重要的生命周期函数" class="headerlink" title="2.3 重要的生命周期函数"></a>2.3 重要的生命周期函数</h4><ul>
<li>1.onAttachedToWindow()</li>
</ul>
<p>表示自身View 被父View通过addView添加到窗口中。<br>在这里，可以通过id获取同一布局文件的其他兄弟View。</p>
<ul>
<li>2.onMeasure()</li>
</ul>
<p>作用：测量View的宽高。<br>Android中，View使用MeasureSpec存放尺寸数据：</p>
<pre><code>    MeasureSpec = mode （前2位值）+ size （后30位值）
    MeasureSpec的值保存在一个int值当中。一个int值有32位，前两位表示模式mode后30位表示大小size        
</code></pre>
<table>
<thead>
<tr>
<th>mode</th>
<th>比较</th>
<th>设置值</th>
</tr>
</thead>
<tbody><tr>
<td>EXACTLY</td>
<td>精准模式，View需要一个精确值 赋予 MeasureSpec的Size</td>
<td>50dp 、 match_parent</td>
</tr>
<tr>
<td>AT_MOST</td>
<td>最大模式，View的尺寸有一个最大值，View不可以超过MeasureSpec当中的Size值</td>
<td>wrap_content</td>
</tr>
<tr>
<td>UNSPECIFIED</td>
<td>无限制,原生View自用，一般不用。</td>
<td>-</td>
</tr>
</tbody></table>
<p>正确设置自定义View尺寸</p>
<pre class="highlight"><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">onMeasure</span><span class="params">(<span class="keyword">int</span> widthMeasureSpec, <span class="keyword">int</span> heightMeasureSpec)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">super</span>.onMeasure(widthMeasureSpec, heightMeasureSpec);</span><br><span class="line">    </span><br><span class="line">    <span class="comment">//==============================================  </span></span><br><span class="line">    <span class="comment">//========= 设置自定义View的默认大小 ==============</span></span><br><span class="line">    <span class="comment">//==============================================</span></span><br><span class="line">    <span class="comment">//取出值</span></span><br><span class="line">    <span class="keyword">int</span> width = getCalSize(<span class="number">100</span>, widthMeasureSpec);</span><br><span class="line">    <span class="keyword">int</span> height = getCalSize(<span class="number">100</span>, heightMeasureSpec);</span><br><span class="line"></span><br><span class="line">    <span class="comment">//处理值</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">//设置</span></span><br><span class="line">    setMeasuredDimension(width, height);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">int</span> <span class="title">getCalSize</span><span class="params">(<span class="keyword">int</span> defaultSize, <span class="keyword">int</span> measureSpec)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">int</span> calSize = defaultSize;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">int</span> mode = MeasureSpec.getMode(measureSpec);<span class="comment">//获取模式</span></span><br><span class="line">    <span class="keyword">int</span> size = MeasureSpec.getSize(measureSpec);<span class="comment">//获取大小</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">switch</span> (mode) &#123;</span><br><span class="line">        <span class="keyword">case</span> MeasureSpec.UNSPECIFIED: <span class="comment">//未指定，就设置为默认大小</span></span><br><span class="line">            calSize = defaultSize;</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">case</span> MeasureSpec.AT_MOST: <span class="comment">//最大取值，将大小取最大值，也可设置自定义View的mMaxHeight / mMaxWidth</span></span><br><span class="line">            calSize = size;</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">case</span> MeasureSpec.EXACTLY: <span class="comment">//指定值</span></span><br><span class="line">            calSize = size;</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">        <span class="keyword">default</span>:</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> calSize;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre>

<ul>
<li>3.onLayout()</li>
</ul>
<p>此方法用于分配子Viewde 布局位置、大小，【组合式】自定义View按需重写，在【自绘式】自定义View中不需要重写。</p>
<ul>
<li>4.onDraw()</li>
</ul>
<p>使用Canvas【画布】和Paint【画笔】绘制所需View样式UI</p>
<p><strong>注意</strong><br>onDraw()是耗时方法，Google官方不建议在该方法内声明新对象，否则会产生作用重复的对象，拉低运行效率。本质就是开辟过多【用完不要】的内存空间，最后得GC回收，影响绘制、运行。</p>
<p>正确用法</p>
<pre class="highlight"><span class="line"><span class="comment">//1.声明重复使用的画笔</span></span><br><span class="line"><span class="keyword">private</span> Paint mPaint;</span><br><span class="line"><span class="comment">//2.初始化</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="title">AlwaysMarqueeTextView</span><span class="params">(Context context,.......)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">super</span>(context, attrs);</span><br><span class="line">        mPaint = <span class="keyword">new</span> Paint();</span><br><span class="line">    &#125;</span><br><span class="line"><span class="comment">//3.多次使用</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">onDraw</span><span class="params">(Canvas canvas)</span> </span>&#123;</span><br><span class="line">        <span class="comment">//添加下划线</span></span><br><span class="line">        <span class="keyword">super</span>.onDraw(canvas);</span><br><span class="line">        mPaint.setColor(Color.GREEN);</span><br><span class="line">        canvas.drawLine( <span class="number">0</span> ,getHeight()/<span class="number">2</span> ,getWidth() ,getHeight()/<span class="number">2</span> , mPaint);</span><br><span class="line">    &#125;</span><br></pre>

<h4 id="2-4-View的自刷新"><a href="#2-4-View的自刷新" class="headerlink" title="2.4 View的自刷新"></a>2.4 View的自刷新</h4><ul>
<li>1.invalidate() 与 requestLayout区别<table>
<thead>
<tr>
<th>重绘</th>
<th>比较</th>
</tr>
</thead>
<tbody><tr>
<td>invalidate()</td>
<td>轻量刷新，更新文本、颜色、状态等，只调用 三大周期方法之一 onDraw()</td>
</tr>
<tr>
<td>requestLayout</td>
<td>重量、约等于重新绘制 重新调用onMeasure() onLayout() onDraw() 绘制</td>
</tr>
</tbody></table>
</li>
<li>2.View自刷新动画</li>
</ul>
<pre class="highlight"><span class="line"></span><br><span class="line"><span class="keyword">int</span> mViewRadius = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">int</span> mViewRadiusReal = <span class="number">50</span>;</span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">anim</span><span class="params">()</span></span>&#123;</span><br><span class="line">    ValueAnimator anim = ValueAnimator.ofInt(<span class="number">0</span>, <span class="number">100</span>);</span><br><span class="line">    anim.setDuration(<span class="number">200</span>);</span><br><span class="line">    anim.setInterpolator(<span class="keyword">new</span> DecelerateInterpolator());</span><br><span class="line">    anim.addUpdateListener(<span class="keyword">new</span> ValueAnimator.AnimatorUpdateListener() &#123;</span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onAnimationUpdate</span><span class="params">(ValueAnimator animation)</span> </span>&#123;</span><br><span class="line">            <span class="keyword">int</span> newRadius = (<span class="keyword">int</span>) animation.getAnimatedValue();</span><br><span class="line">            mViewRadius = mViewRadiusReal * newRadius /<span class="number">100</span>;</span><br><span class="line">            invalidate();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;);</span><br><span class="line">&#125;</span><br></pre>



<h1 id="3-实践"><a href="#3-实践" class="headerlink" title="3. 实践"></a>3. 实践</h1><p>该例未完善，仅供学习，如需使用，需自行扩展</p>
<img src='https://gitee.com/cupster/picbed/raw/master/uPic/20210105174951_bIMCIF.png' />


<h4 id="3-1声明自定义属性"><a href="#3-1声明自定义属性" class="headerlink" title="3.1声明自定义属性"></a>3.1声明自定义属性</h4><p>/res/values/attrs.xml文件新增</p>
<pre class="highlight"><span class="line"><span class="tag">&lt;<span class="name">resources</span>&gt;</span></span><br><span class="line">    <span class="comment">&lt;!--xml快捷属性--&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">declare-styleable</span> <span class="attr">name</span>=<span class="string">&quot;DiyView&quot;</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">attr</span> <span class="attr">name</span>=<span class="string">&quot;chart_x_color&quot;</span> <span class="attr">format</span>=<span class="string">&quot;color&quot;</span> /&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">attr</span> <span class="attr">name</span>=<span class="string">&quot;chart_line_color&quot;</span> <span class="attr">format</span>=<span class="string">&quot;color&quot;</span> /&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">attr</span> <span class="attr">name</span>=<span class="string">&quot;chart_line_width&quot;</span> <span class="attr">format</span>=<span class="string">&quot;dimension&quot;</span>/&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">attr</span> <span class="attr">name</span>=<span class="string">&quot;chart_line_space&quot;</span> <span class="attr">format</span>=<span class="string">&quot;dimension&quot;</span>/&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">declare-styleable</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">resources</span>&gt;</span></span><br></pre>
<h4 id="3-2-自定义View类java文件"><a href="#3-2-自定义View类java文件" class="headerlink" title="3.2 自定义View类java文件"></a>3.2 自定义View类java文件</h4><pre class="highlight"><span class="line"><span class="keyword">package</span> com.cupster.base_super_resource;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> android.content.Context;</span><br><span class="line"><span class="keyword">import</span> android.content.res.TypedArray;</span><br><span class="line"><span class="keyword">import</span> android.graphics.Canvas;</span><br><span class="line"><span class="keyword">import</span> android.graphics.Color;</span><br><span class="line"><span class="keyword">import</span> android.graphics.Paint;</span><br><span class="line"><span class="keyword">import</span> android.graphics.Path;</span><br><span class="line"><span class="keyword">import</span> android.os.Build;</span><br><span class="line"><span class="keyword">import</span> android.util.AttributeSet;</span><br><span class="line"><span class="keyword">import</span> android.util.Log;</span><br><span class="line"><span class="keyword">import</span> android.view.View;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.util.Map;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> androidx.annotation.Nullable;</span><br><span class="line"><span class="keyword">import</span> androidx.annotation.RequiresApi;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">DiyView</span> <span class="keyword">extends</span> <span class="title">View</span> </span>&#123;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">DiyView</span><span class="params">(Context context)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">super</span>(context);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">DiyView</span><span class="params">(Context context, <span class="meta">@Nullable</span> AttributeSet attrs)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">super</span>(context, attrs);</span><br><span class="line">        initAttrs(context, attrs);</span><br><span class="line">        mPaint = <span class="keyword">new</span> Paint();</span><br><span class="line">        mPath = <span class="keyword">new</span> Path();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">DiyView</span><span class="params">(Context context, <span class="meta">@Nullable</span> AttributeSet attrs, <span class="keyword">int</span> defStyleAttr)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">super</span>(context, attrs, defStyleAttr);</span><br><span class="line">        initAttrs(context, attrs);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">DiyView</span><span class="params">(Context context, <span class="meta">@Nullable</span> AttributeSet attrs, <span class="keyword">int</span> defStyleAttr, <span class="keyword">int</span> defStyleRes)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">super</span>(context, attrs, defStyleAttr, defStyleRes);</span><br><span class="line">        initAttrs(context, attrs);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">private</span> Paint mPaint;</span><br><span class="line">    <span class="keyword">private</span> Path mPath;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">int</span> chart_x_color = Color.parseColor(<span class="string">&quot;#808080&quot;</span>);</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">int</span> chart_line_color = Color.parseColor(<span class="string">&quot;#6ce4d8&quot;</span>);</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">float</span> chart_line_width = <span class="number">10.0f</span>;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">float</span> space = <span class="number">10.0f</span>;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">float</span> zeroX = <span class="number">120f</span>;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">float</span> zeroY = <span class="number">120f</span>;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">float</span> axisY = <span class="number">500f</span>;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">float</span> axisX = <span class="number">900f</span>;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">float</span> scalX = <span class="number">1.0f</span>;<span class="comment">//TODO 扩展：数据缩放呈现</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">float</span> scalY = <span class="number">1.0f</span>;<span class="comment">//TODO 扩展：数据缩放呈现</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">float</span>[] lines = <span class="keyword">new</span> <span class="keyword">float</span>[]&#123;<span class="number">0</span>,<span class="number">0</span>,<span class="number">0</span>,<span class="number">0</span>,<span class="number">0</span>,<span class="number">0</span>&#125;;</span><br><span class="line">    <span class="keyword">private</span> String[] xLabs = <span class="keyword">new</span> String[]&#123;<span class="string">&quot;1月&quot;</span>,<span class="string">&quot;2月&quot;</span>,<span class="string">&quot;3月&quot;</span>,<span class="string">&quot;4月&quot;</span>,<span class="string">&quot;5月&quot;</span>,<span class="string">&quot;6月&quot;</span>&#125;;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">initAttrs</span><span class="params">(Context context, AttributeSet attrs)</span> </span>&#123;</span><br><span class="line">        <span class="comment">//1.获取属性数组</span></span><br><span class="line">        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.DiyView);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//2.获取属性值</span></span><br><span class="line">        chart_x_color = typedArray.getColor(R.styleable.DiyView_chart_x_color, Color.parseColor(<span class="string">&quot;#404040&quot;</span>));</span><br><span class="line">        chart_line_color = typedArray.getColor(R.styleable.DiyView_chart_line_color, Color.parseColor(<span class="string">&quot;#0000ee&quot;</span>));</span><br><span class="line">        chart_line_width = typedArray.getDimension(R.styleable.DiyView_chart_line_width, <span class="number">10.0f</span>);</span><br><span class="line">        space = typedArray.getDimension(R.styleable.DiyView_chart_line_space, <span class="number">10.0f</span>);</span><br><span class="line">        typedArray.recycle();</span><br><span class="line">        <span class="comment">//初始化假数据</span></span><br><span class="line"><span class="comment">//        lines = new float[]&#123;</span></span><br><span class="line"><span class="comment">//                //view的左上角为坐标系的（0，0）</span></span><br><span class="line"><span class="comment">//                //line1: x1：【原点x轴坐标值zeroX + 间距space +柱形宽的一半值chart_line_width/2】，y1：【原点y轴坐标值=y轴高度值axisY+y轴顶点在View坐标系的Y轴值zeroY】</span></span><br><span class="line"><span class="comment">//                //       x2：【原点x轴坐标值zeroX + 间距space +柱形宽的一半值chart_line_width/2】，y2：【原点y轴坐标值 - 要绘制的条形图高度-此处为假数据】</span></span><br><span class="line"><span class="comment">//                zeroX + space + chart_line_width / 2, axisY + zeroY, zeroX + space + chart_line_width / 2, axisY + zeroY - 20 * 1,</span></span><br><span class="line"><span class="comment">//                //line2: x1：【原点x轴坐标值zeroX + 间距space +柱形宽的一半值chart_line_width/2 +[第一个条形图的宽chart_line_width+第二个间距space] 】，y1：【原点y轴坐标值=y轴高度值axisY+y轴顶点在View坐标系的Y轴值zeroY】</span></span><br><span class="line"><span class="comment">//                //       x2 = x1  ,y2 = axisY+zeroY - 要绘制的高度值</span></span><br><span class="line"><span class="comment">//                zeroX + space + chart_line_width + space + chart_line_width / 2, axisY + zeroY, zeroX + space + chart_line_width + space + chart_line_width / 2, axisY + zeroY - 20 * 2,</span></span><br><span class="line"><span class="comment">//                zeroX + space + chart_line_width + space + chart_line_width + space + chart_line_width / 2, axisY + zeroY, zeroX + space + chart_line_width + space + chart_line_width + space + chart_line_width / 2, axisY + zeroY - 20 * 3,</span></span><br><span class="line"><span class="comment">//                zeroX + space + chart_line_width * 3 + space * 3 + chart_line_width / 2, axisY + zeroY, zeroX + space + chart_line_width * 3 + space * 3 + chart_line_width / 2, axisY + zeroY - 20 * 4,</span></span><br><span class="line"><span class="comment">//                zeroX + space + chart_line_width * 4 + space * 4 + chart_line_width / 2, axisY + zeroY, zeroX + space + chart_line_width * 4 + space * 4 + chart_line_width / 2, axisY + zeroY - 20 * 5,</span></span><br><span class="line"><span class="comment">//                zeroX + space + chart_line_width * 5 + space * 5 + chart_line_width / 2, axisY + zeroY, zeroX + space + chart_line_width * 5 + space * 5 + chart_line_width / 2, axisY + zeroY - 20 * 6,</span></span><br><span class="line"><span class="comment">//                zeroX + space + chart_line_width * 6 + space * 6 + chart_line_width / 2, axisY + zeroY, zeroX + space + chart_line_width * 6 + space * 6 + chart_line_width / 2, axisY + zeroY - 20 * 7,</span></span><br><span class="line"><span class="comment">//                zeroX + space * (1 + 7) + chart_line_width * 7 + chart_line_width / 2, axisY + zeroY, zeroX + space * (1 + 7) + chart_line_width * 7 + chart_line_width / 2, axisY + zeroY - 20 * 8,</span></span><br><span class="line"><span class="comment">//                //line9: x1：【原点x轴坐标值zeroX + 间距space * n +柱形宽的一半值chart_line_width *(n-1)+柱形宽的一半值chart_line_width/2 】，y1：【原点y轴坐标值=y轴高度值axisY+y轴顶点在View坐标系的Y轴值zeroY】</span></span><br><span class="line"><span class="comment">//                //       x2 = x1  ,y2 = axisY+zeroY - 要绘制的高度值</span></span><br><span class="line"><span class="comment">//                zeroX + space * (9) + chart_line_width * (9 - 1) + chart_line_width / 2, axisY + zeroY, zeroX + space * (9) + chart_line_width * (9 - 1) + chart_line_width / 2, axisY + zeroY - 20 * 9,</span></span><br><span class="line"><span class="comment">//                zeroX + space * 10 + chart_line_width * (10 - 1) + chart_line_width / 2, axisY + zeroY, zeroX + space * (10) + chart_line_width * (10 - 1) + chart_line_width / 2, axisY + zeroY - 20 * 10,</span></span><br><span class="line"><span class="comment">//                zeroX + space * 11 + chart_line_width * (11 - 1) + chart_line_width / 2, axisY + zeroY, zeroX + space * (11) + chart_line_width * (11 - 1) + chart_line_width / 2, axisY + zeroY - 20 * 11,</span></span><br><span class="line"><span class="comment">//                zeroX + space * 12 + chart_line_width * (12 - 1) + chart_line_width / 2, axisY + zeroY, zeroX + space * (12) + chart_line_width * (12 - 1) + chart_line_width / 2, axisY + zeroY - 20 * 12</span></span><br><span class="line"><span class="comment">//                //lineX：最终固定公式为</span></span><br><span class="line"><span class="comment">//                // //line9: x1：【zeroX + space * n +chart_line_width*(n-0.5f) 】，y1：【axisY+zeroY】</span></span><br><span class="line"><span class="comment">//                //          x2 = x1                                            ,y2 = axisY+zeroY - 要绘制的高度值</span></span><br><span class="line"><span class="comment">//                //          x1                                y1                         x2                                    y2</span></span><br><span class="line"><span class="comment">//                , zeroX + space * 13 + chart_line_width * 12.5f, axisY + zeroY, zeroX + space * 13 + chart_line_width * 12.5f, axisY + zeroY - 111</span></span><br><span class="line"><span class="comment">//        &#125;;</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">onAttachedToWindow</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">super</span>.onAttachedToWindow();</span><br><span class="line">        Log.d(<span class="string">&quot;DiyView&quot;</span>, <span class="string">&quot;onAttachedToWindow&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//    @Override</span></span><br><span class="line"><span class="comment">//    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) &#123;</span></span><br><span class="line"><span class="comment">//        super.onMeasure(widthMeasureSpec, heightMeasureSpec);</span></span><br><span class="line"><span class="comment">//        Log.d(&quot;DiyView&quot;,&quot;onMeasure&quot;);</span></span><br><span class="line"><span class="comment">//        //==============================================</span></span><br><span class="line"><span class="comment">//        //========= 设置自定义View的默认大小 ==============</span></span><br><span class="line"><span class="comment">//        //==============================================</span></span><br><span class="line"><span class="comment">//        //取出值</span></span><br><span class="line"><span class="comment">//        int width = getCalSize(600, widthMeasureSpec);</span></span><br><span class="line"><span class="comment">//        int height = getCalSize(300, heightMeasureSpec);</span></span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">//        //TODO 处理值</span></span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">//        //设置</span></span><br><span class="line"><span class="comment">//        setMeasuredDimension(width, height);</span></span><br><span class="line"><span class="comment">//    &#125;</span></span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">//    private int getCalSize(int defaultSize, int measureSpec) &#123;</span></span><br><span class="line"><span class="comment">//        int calSize = defaultSize;</span></span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">//        int mode = MeasureSpec.getMode(measureSpec);//获取模式</span></span><br><span class="line"><span class="comment">//        int size = MeasureSpec.getSize(measureSpec);//获取大小</span></span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">//        switch (mode) &#123;</span></span><br><span class="line"><span class="comment">//            case MeasureSpec.UNSPECIFIED: //未指定，就设置为默认大小</span></span><br><span class="line"><span class="comment">//                calSize = defaultSize;</span></span><br><span class="line"><span class="comment">//                break;</span></span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">//            case MeasureSpec.AT_MOST: //最大取值，将大小取最大值，也可设置自定义View的mMaxHeight / mMaxWidth</span></span><br><span class="line"><span class="comment">//                calSize = size;</span></span><br><span class="line"><span class="comment">//                break;</span></span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">//            case MeasureSpec.EXACTLY: //指定值</span></span><br><span class="line"><span class="comment">//                calSize = size;</span></span><br><span class="line"><span class="comment">//                break;</span></span><br><span class="line"><span class="comment">//            default:</span></span><br><span class="line"><span class="comment">//                break;</span></span><br><span class="line"><span class="comment">//        &#125;</span></span><br><span class="line"><span class="comment">//        return calSize;</span></span><br><span class="line"><span class="comment">//    &#125;</span></span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">onLayout</span><span class="params">(<span class="keyword">boolean</span> changed, <span class="keyword">int</span> left, <span class="keyword">int</span> top, <span class="keyword">int</span> right, <span class="keyword">int</span> bottom)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">super</span>.onLayout(changed, left, top, right, bottom);</span><br><span class="line">        Log.d(<span class="string">&quot;DiyView&quot;</span>, <span class="string">&quot;onLayout&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">onDraw</span><span class="params">(Canvas canvas)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">super</span>.onDraw(canvas);</span><br><span class="line">        Log.d(<span class="string">&quot;DiyView&quot;</span>, <span class="string">&quot;onDraw&quot;</span>);</span><br><span class="line"></span><br><span class="line">        mPaint.reset();</span><br><span class="line">        mPath.reset();</span><br><span class="line">        <span class="comment">//1.画坐标轴</span></span><br><span class="line">        mPaint.setColor(Color.parseColor(<span class="string">&quot;#404040&quot;</span>));</span><br><span class="line">        mPaint.setStyle(Paint.Style.STROKE);</span><br><span class="line">        mPath.moveTo(zeroX, zeroY);<span class="comment">//y轴顶点(120,120)</span></span><br><span class="line">        mPath.rLineTo(<span class="number">0</span>, axisY);<span class="comment">//y轴方向</span></span><br><span class="line">        mPath.rLineTo(axisX, <span class="number">0</span>);<span class="comment">//x轴方向</span></span><br><span class="line">        canvas.drawPath(mPath, mPaint);<span class="comment">//绘制</span></span><br><span class="line">        <span class="comment">//2.画x轴label</span></span><br><span class="line">        mPaint.reset();</span><br><span class="line">        mPaint.setColor(chart_x_color);</span><br><span class="line">        mPaint.setTextSize(<span class="number">18</span>);<span class="comment">//px</span></span><br><span class="line">        mPaint.setStyle(Paint.Style.FILL);</span><br><span class="line">        <span class="comment">//假数据</span></span><br><span class="line"><span class="comment">//        canvas.drawText(&quot;1月&quot;, zeroX + space, zeroY + axisY + 28, mPaint);</span></span><br><span class="line"><span class="comment">//        canvas.drawText(&quot;2月&quot;, zeroX + space * 2 + chart_line_width * (2 - 1), zeroY + axisY + 28, mPaint);</span></span><br><span class="line"><span class="comment">//        canvas.drawText(&quot;3月&quot;, zeroX + space * 3 + chart_line_width * (3 - 1), zeroY + axisY + 28, mPaint);</span></span><br><span class="line"><span class="comment">//        canvas.drawText(&quot;4月&quot;, zeroX + space * 4 + chart_line_width * (4 - 1), zeroY + axisY + 28, mPaint);</span></span><br><span class="line"><span class="comment">//        canvas.drawText(&quot;5月&quot;, zeroX + space * 5 + chart_line_width * (5 - 1), zeroY + axisY + 28, mPaint);</span></span><br><span class="line"><span class="comment">//        canvas.drawText(&quot;6月&quot;, zeroX + space * 6 + chart_line_width * (6 - 1), zeroY + axisY + 28, mPaint);</span></span><br><span class="line"><span class="comment">//        canvas.drawText(&quot;7月&quot;, zeroX + space * 7 + chart_line_width * (7 - 1), zeroY + axisY + 28, mPaint);</span></span><br><span class="line"><span class="comment">//        canvas.drawText(&quot;8月&quot;, zeroX + space * 8 + chart_line_width * (8 - 1), zeroY + axisY + 28, mPaint);</span></span><br><span class="line"><span class="comment">//        canvas.drawText(&quot;9月&quot;, zeroX + space * 9 + chart_line_width * (9 - 1), zeroY + axisY + 28, mPaint);</span></span><br><span class="line"><span class="comment">//        canvas.drawText(&quot;10月&quot;, zeroX + space * 10 + chart_line_width * (10 - 1), zeroY + axisY + 28, mPaint);</span></span><br><span class="line"><span class="comment">//        canvas.drawText(&quot;11月&quot;, zeroX + space * 11 + chart_line_width * (11 - 1), zeroY + axisY + 28, mPaint);</span></span><br><span class="line"><span class="comment">//        canvas.drawText(&quot;12月&quot;, zeroX + space * 12 + chart_line_width * (12 - 1), zeroY + axisY + 28, mPaint);</span></span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span> ; i &lt; xLabs.length ; i++)&#123;</span><br><span class="line">            canvas.drawText(xLabs[i], zeroX + space*(i+<span class="number">1</span>) + i*chart_line_width, zeroY + axisY + <span class="number">28</span>, mPaint);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">//3.画条形图</span></span><br><span class="line">        <span class="keyword">if</span> (lines==<span class="keyword">null</span> || lines.length&lt;<span class="number">1</span>)&#123;</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        mPaint.reset();</span><br><span class="line">        mPaint.setColor(chart_line_color);</span><br><span class="line">        mPaint.setStrokeWidth(chart_line_width);</span><br><span class="line">        canvas.drawLines(lines, mPaint);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setHeightArr</span><span class="params">(Map&lt;String ,Float&gt; datas)</span> </span>&#123;</span><br><span class="line">        <span class="comment">//0.判空</span></span><br><span class="line">        <span class="keyword">if</span> (datas == <span class="keyword">null</span> || datas.size() &lt; <span class="number">1</span>) &#123;</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">//1.取数据</span></span><br><span class="line">        <span class="keyword">float</span>[] arr = <span class="keyword">new</span> <span class="keyword">float</span>[datas.size()];</span><br><span class="line">        xLabs = <span class="keyword">new</span> String[datas.size()];</span><br><span class="line">        <span class="keyword">int</span> i = <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">for</span> (Map.Entry entry : datas.entrySet())&#123;</span><br><span class="line">            xLabs[i] = entry.getKey()==<span class="keyword">null</span> ? <span class="string">&quot;&quot;</span>:(String) entry.getKey();</span><br><span class="line">            arr[i] = entry.getValue() ==<span class="keyword">null</span>? <span class="number">0.0f</span> :(<span class="keyword">float</span>) entry.getValue();</span><br><span class="line">            i++;</span><br><span class="line">        &#125;</span><br><span class="line">        lines = <span class="keyword">new</span> <span class="keyword">float</span>[arr.length * <span class="number">4</span>];</span><br><span class="line">        <span class="comment">//2.检查最大值，变更Y轴上限</span></span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> n = <span class="number">0</span>; n &lt; arr.length; n++) &#123;</span><br><span class="line">            <span class="keyword">if</span> (arr[n] &gt; axisY) &#123;</span><br><span class="line">                axisY = arr[n] +<span class="number">20</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">//3.装载条形图数据</span></span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> n = <span class="number">0</span>; n &lt; arr.length; n++) &#123;</span><br><span class="line">            lines[<span class="number">4</span>*n]     = zeroX + space * (n+<span class="number">1</span>) + chart_line_width * ((n+<span class="number">1</span>) - <span class="number">1</span> + <span class="number">0.5f</span>);</span><br><span class="line">            lines[<span class="number">1</span> + <span class="number">4</span>*n] = axisY + zeroY;</span><br><span class="line">            lines[<span class="number">2</span> + <span class="number">4</span>*n] = zeroX + space * (n+<span class="number">1</span>) + chart_line_width * ((n+<span class="number">1</span>) - <span class="number">1</span> + <span class="number">0.5f</span>);</span><br><span class="line">            lines[<span class="number">3</span> + <span class="number">4</span>*n] = axisY + zeroY - arr[n];</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">//通知更新View</span></span><br><span class="line">        invalidate();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre>

<h4 id="3-3-布局文件使用"><a href="#3-3-布局文件使用" class="headerlink" title="3.3 布局文件使用"></a>3.3 布局文件使用</h4><pre class="highlight"><span class="line">&lt;com.cupster.base_super_resource.DiyView</span><br><span class="line">         android:id=&quot;@+id/diy_chart&quot;</span><br><span class="line"></span><br><span class="line">         android:layout_width=&quot;match_parent&quot;</span><br><span class="line">         android:layout_height=&quot;wrap_content&quot;</span><br><span class="line">         app:chart_line_color=&quot;#3ca1f9&quot;</span><br><span class="line">         app:chart_line_width=&quot;12dp&quot;</span><br><span class="line">         app:chart_line_space=&quot;5dp&quot;</span><br><span class="line">         app:chart_x_color=&quot;#ff8696&quot;</span><br><span class="line">         /&gt;</span><br></pre>

<h4 id="3-4-Activity中使用"><a href="#3-4-Activity中使用" class="headerlink" title="3.4 Activity中使用"></a>3.4 Activity中使用</h4><pre class="highlight"><span class="line"><span class="keyword">package</span> com.cupster.base_super_resource;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> android.os.Bundle;</span><br><span class="line"><span class="keyword">import</span> android.util.ArrayMap;</span><br><span class="line"><span class="keyword">import</span> java.util.Map;</span><br><span class="line"><span class="keyword">import</span> androidx.appcompat.app.AppCompatActivity;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">BaseSuperResourceActivity</span> <span class="keyword">extends</span> <span class="title">AppCompatActivity</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    DiyView diyChart;</span><br><span class="line">    Map&lt;String ,Float&gt; datas = <span class="keyword">new</span> ArrayMap&lt;&gt;();</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">onCreate</span><span class="params">(Bundle savedInstanceState)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">super</span>.onCreate(savedInstanceState);</span><br><span class="line">        setContentView(R.layout.activity_base_super_resource);</span><br><span class="line"></span><br><span class="line">         diyChart = findViewById(R.id.diy_chart);</span><br><span class="line"></span><br><span class="line">         datas.put(<span class="string">&quot;1月&quot;</span>,<span class="number">100f</span>);</span><br><span class="line">         datas.put(<span class="string">&quot;2月&quot;</span>,<span class="number">200f</span>);</span><br><span class="line">         datas.put(<span class="string">&quot;3月&quot;</span>,<span class="number">300f</span>);</span><br><span class="line">         datas.put(<span class="string">&quot;4月&quot;</span>,<span class="number">500f</span>);</span><br><span class="line">         datas.put(<span class="string">&quot;5月&quot;</span>,<span class="number">50f</span>);</span><br><span class="line">         datas.put(<span class="string">&quot;6月&quot;</span>,<span class="number">400f</span>);</span><br><span class="line">         datas.put(<span class="string">&quot;7月&quot;</span>,<span class="number">200f</span>);</span><br><span class="line">         datas.put(<span class="string">&quot;8月&quot;</span>,<span class="number">30f</span>);</span><br><span class="line">         datas.put(<span class="string">&quot;9月&quot;</span>,<span class="number">180f</span>);</span><br><span class="line">         datas.put(<span class="string">&quot;10月&quot;</span>,<span class="number">330f</span>);</span><br><span class="line">         datas.put(<span class="string">&quot;11月&quot;</span>,<span class="number">20f</span>);</span><br><span class="line">         datas.put(<span class="string">&quot;12月&quot;</span>,<span class="number">400f</span>);</span><br><span class="line"></span><br><span class="line">        diyChart.postDelayed(<span class="keyword">new</span> Runnable() &#123;</span><br><span class="line">            <span class="meta">@Override</span></span><br><span class="line">            <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>&#123;</span><br><span class="line">                diyChart.setHeightArr(datas);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;,<span class="number">5000</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre>



















  </div>
  <div>
  
  <div class="post-note note-warning copyright" style="margin-top: 42px">
    <p><span style="font-weight: bold;">作者：</span><a target="_blank" rel="nofollow noopener noreferrer" href="http://example.com/about">Cupster</a></p>
    <p><span style="font-weight: bold;">文章链接：</span><a target="_blank" rel="nofollow noopener noreferrer" href="http://example.com/2019/06/29/009-Android%E8%87%AA%E5%AE%9A%E4%B9%89View(4)%EF%BC%9A%E8%87%AA%E7%BB%98%E5%BC%8F%E8%AF%A6%E8%A7%A3(%E5%9B%BE%E6%96%87)/">http://example.com/2019/06/29/009-Android%E8%87%AA%E5%AE%9A%E4%B9%89View(4)%EF%BC%9A%E8%87%AA%E7%BB%98%E5%BC%8F%E8%AF%A6%E8%A7%A3(%E5%9B%BE%E6%96%87)/</a></p>
    <p><span style="font-weight: bold;">版权声明：</span>本博客所有文章除特别声明外，均采用<a target="_blank" rel="noopener" href="https://creativecommons.org/licenses/by-nc-sa/4.0/deed.zh">CC BY-NC-SA 4.0 协议</a>。转载请注明出处！</p>
  </div>
  
  </div>
</article>
<div class="nav">
  
  <div class="nav-item-prev">
    <a href="/2019/07/01/010-Android自定义View(5)：画布Canvas详解(图文)/" class="nav-link">
      <i class="iconfont icon-left nav-prev-icon"></i>
      <div>
        <div class="nav-label">上一篇</div>
        
        <div class="nav-title">010-Android自定义View(5)：画布Canvas详解(图文) </div>
        
      </div>
    </a>
  </div>
  
  
  <div class="nav-item-next">
    <a href="/2019/06/26/008-Android自定义View(3)：衍生（扩展式）详解(图文)/" class="nav-link">
      <div>
        <div class="nav-label">下一篇</div>
        
        <div class="nav-title">008-Android自定义View(3)：衍生(扩展式)详解(图文) </div>
        
      </div>
      <i class="iconfont icon-right nav-next-icon"></i>
    </a>
  </div>
  
</div>

<div class="card card-content toc-card" id="mobiletoc">
  <div class="toc-header"><i class="iconfont icon-menu" style="padding-right: 2px;"></i>目录</div>
<ol class="toc"><li class="toc-item toc-level-1"><a class="toc-link" href="#1-%E7%AE%80%E8%BF%B0"><span class="toc-text">1. 简述</span></a></li><li class="toc-item toc-level-1"><a class="toc-link" href="#2-%E5%89%8D%E6%8F%90%E5%9F%BA%E7%A1%80"><span class="toc-text">2. 前提基础</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#2-1%E4%BA%86%E8%A7%A3View%E5%85%B3%E8%81%94Activity%E6%97%B6%E7%9A%84%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F"><span class="toc-text">2.1了解View关联Activity时的生命周期</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#2-2-%E4%BA%86%E8%A7%A3%E8%87%AA%E5%AE%9A%E4%B9%89View%E7%9A%84%E8%87%AA%E5%AE%9A%E4%B9%89%E5%B1%9E%E6%80%A7%EF%BC%8C%E4%BB%A5%E5%8F%8A%E8%8E%B7%E5%8F%96%E3%80%81%E4%BD%BF%E7%94%A8"><span class="toc-text">2.2 了解自定义View的自定义属性，以及获取、使用</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#2-3-%E9%87%8D%E8%A6%81%E7%9A%84%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F%E5%87%BD%E6%95%B0"><span class="toc-text">2.3 重要的生命周期函数</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#2-4-View%E7%9A%84%E8%87%AA%E5%88%B7%E6%96%B0"><span class="toc-text">2.4 View的自刷新</span></a></li></ol></li></ol></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#3-%E5%AE%9E%E8%B7%B5"><span class="toc-text">3. 实践</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#3-1%E5%A3%B0%E6%98%8E%E8%87%AA%E5%AE%9A%E4%B9%89%E5%B1%9E%E6%80%A7"><span class="toc-text">3.1声明自定义属性</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#3-2-%E8%87%AA%E5%AE%9A%E4%B9%89View%E7%B1%BBjava%E6%96%87%E4%BB%B6"><span class="toc-text">3.2 自定义View类java文件</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#3-3-%E5%B8%83%E5%B1%80%E6%96%87%E4%BB%B6%E4%BD%BF%E7%94%A8"><span class="toc-text">3.3 布局文件使用</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#3-4-Activity%E4%B8%AD%E4%BD%BF%E7%94%A8"><span class="toc-text">3.4 Activity中使用</span></a></li></ol></li></ol></li></ol></li></ol>
</div></main>
          <aside class="left-column">
            
            <div class="card card-author">
              
<img src="/img/ic_head.jpg" class="author-img">

<p class="author-name">Cupster</p>
<p class="author-description">深山磨柴斧，一鸣震江湖</p>
<div class="author-message">
  <a class="author-posts-count" href="/archives">
    <span>66</span>
    <span>文章</span>
  </a>
  <a class="author-categories-count" href="/categories">
    <span>5</span>
    <span>分类</span>
  </a>
  <a class="author-tags-count" href="/tags">
    <span>31</span>
    <span>标签</span>
  </a>
</div>

<div class="author-card-society">
  
    <div class="author-card-society-icon">
      <a target="_blank" rel="noopener" href="https://cupster.blog.csdn.net/">
        <i class="iconfont /img/ic_csdn.png society-icon"></i>
      </a>
    </div>
  
</div>

            </div>
            
            <div class="sticky-tablet">
  
  
  <article class="display-when-two-columns spacer">
    <div class="card card-content toc-card">
      <div class="toc-header"><i class="iconfont icon-menu" style="padding-right: 2px;"></i>目录</div>
<ol class="toc"><li class="toc-item toc-level-1"><a class="toc-link" href="#1-%E7%AE%80%E8%BF%B0"><span class="toc-text">1. 简述</span></a></li><li class="toc-item toc-level-1"><a class="toc-link" href="#2-%E5%89%8D%E6%8F%90%E5%9F%BA%E7%A1%80"><span class="toc-text">2. 前提基础</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#2-1%E4%BA%86%E8%A7%A3View%E5%85%B3%E8%81%94Activity%E6%97%B6%E7%9A%84%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F"><span class="toc-text">2.1了解View关联Activity时的生命周期</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#2-2-%E4%BA%86%E8%A7%A3%E8%87%AA%E5%AE%9A%E4%B9%89View%E7%9A%84%E8%87%AA%E5%AE%9A%E4%B9%89%E5%B1%9E%E6%80%A7%EF%BC%8C%E4%BB%A5%E5%8F%8A%E8%8E%B7%E5%8F%96%E3%80%81%E4%BD%BF%E7%94%A8"><span class="toc-text">2.2 了解自定义View的自定义属性，以及获取、使用</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#2-3-%E9%87%8D%E8%A6%81%E7%9A%84%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F%E5%87%BD%E6%95%B0"><span class="toc-text">2.3 重要的生命周期函数</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#2-4-View%E7%9A%84%E8%87%AA%E5%88%B7%E6%96%B0"><span class="toc-text">2.4 View的自刷新</span></a></li></ol></li></ol></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#3-%E5%AE%9E%E8%B7%B5"><span class="toc-text">3. 实践</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#3-1%E5%A3%B0%E6%98%8E%E8%87%AA%E5%AE%9A%E4%B9%89%E5%B1%9E%E6%80%A7"><span class="toc-text">3.1声明自定义属性</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#3-2-%E8%87%AA%E5%AE%9A%E4%B9%89View%E7%B1%BBjava%E6%96%87%E4%BB%B6"><span class="toc-text">3.2 自定义View类java文件</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#3-3-%E5%B8%83%E5%B1%80%E6%96%87%E4%BB%B6%E4%BD%BF%E7%94%A8"><span class="toc-text">3.3 布局文件使用</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#3-4-Activity%E4%B8%AD%E4%BD%BF%E7%94%A8"><span class="toc-text">3.4 Activity中使用</span></a></li></ol></li></ol></li></ol></li></ol>
    </div>
  </article>
  
  
  <article class="card card-content categories-widget">
    <div class="categories-card">
  <div class="categories-header"><i class="iconfont icon-fenlei" style="padding-right: 2px;"></i>分类</div>
  <div class="categories-list">
    
      <a href="/categories/Android/">
        <div class="categories-list-item">
          Android
          <span class="categories-list-item-badge">38</span>
        </div>
      </a>
    
      <a href="/categories/Linux/">
        <div class="categories-list-item">
          Linux
          <span class="categories-list-item-badge">6</span>
        </div>
      </a>
    
      <a href="/categories/Dev-Base/">
        <div class="categories-list-item">
          Dev-Base
          <span class="categories-list-item-badge">12</span>
        </div>
      </a>
    
      <a href="/categories/Kotlin/">
        <div class="categories-list-item">
          Kotlin
          <span class="categories-list-item-badge">9</span>
        </div>
      </a>
    
      <a href="/categories/自动化脚本/">
        <div class="categories-list-item">
          自动化脚本
          <span class="categories-list-item-badge">1</span>
        </div>
      </a>
    
  </div>
</div>
  </article>
  
  <article class="card card-content tags-widget">
    <div class="tags-card">
  <div class="tags-header"><i class="iconfont icon-biaoqian" style="padding-right: 2px;"></i>热门标签</div>
  <div class="tags-list">
    
    <a href="/tags/Android/" title="Android"><div class="tags-list-item">Android</div></a>
    
    <a href="/tags/Android自定义View/" title="Android自定义View"><div class="tags-list-item">Android自定义View</div></a>
    
    <a href="/tags/MacOS/" title="MacOS"><div class="tags-list-item">MacOS</div></a>
    
    <a href="/tags/Kotlin/" title="Kotlin"><div class="tags-list-item">Kotlin</div></a>
    
    <a href="/tags/Android持久化/" title="Android持久化"><div class="tags-list-item">Android持久化</div></a>
    
    <a href="/tags/CentOS/" title="CentOS"><div class="tags-list-item">CentOS</div></a>
    
    <a href="/tags/Linux/" title="Linux"><div class="tags-list-item">Linux</div></a>
    
    <a href="/tags/Android事件/" title="Android事件"><div class="tags-list-item">Android事件</div></a>
    
    <a href="/tags/Android基础/" title="Android基础"><div class="tags-list-item">Android基础</div></a>
    
    <a href="/tags/Android手势/" title="Android手势"><div class="tags-list-item">Android手势</div></a>
    
    <a href="/tags/Scrcpy/" title="Scrcpy"><div class="tags-list-item">Scrcpy</div></a>
    
    <a href="/tags/Android动画/" title="Android动画"><div class="tags-list-item">Android动画</div></a>
    
    <a href="/tags/Git/" title="Git"><div class="tags-list-item">Git</div></a>
    
    <a href="/tags/Java/" title="Java"><div class="tags-list-item">Java</div></a>
    
    <a href="/tags/Docker/" title="Docker"><div class="tags-list-item">Docker</div></a>
    
    <a href="/tags/Android-Dialog/" title="Android Dialog"><div class="tags-list-item">Android Dialog</div></a>
    
  </div>
</div>
  </article>
  
  
</div>
          </aside>
          <aside class="right-column">
            <div class="sticky-widescreen">
  
  
  <article class="card card-content toc-card">
    <div class="toc-header"><i class="iconfont icon-menu" style="padding-right: 2px;"></i>目录</div>
<ol class="toc"><li class="toc-item toc-level-1"><a class="toc-link" href="#1-%E7%AE%80%E8%BF%B0"><span class="toc-text">1. 简述</span></a></li><li class="toc-item toc-level-1"><a class="toc-link" href="#2-%E5%89%8D%E6%8F%90%E5%9F%BA%E7%A1%80"><span class="toc-text">2. 前提基础</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#2-1%E4%BA%86%E8%A7%A3View%E5%85%B3%E8%81%94Activity%E6%97%B6%E7%9A%84%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F"><span class="toc-text">2.1了解View关联Activity时的生命周期</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#2-2-%E4%BA%86%E8%A7%A3%E8%87%AA%E5%AE%9A%E4%B9%89View%E7%9A%84%E8%87%AA%E5%AE%9A%E4%B9%89%E5%B1%9E%E6%80%A7%EF%BC%8C%E4%BB%A5%E5%8F%8A%E8%8E%B7%E5%8F%96%E3%80%81%E4%BD%BF%E7%94%A8"><span class="toc-text">2.2 了解自定义View的自定义属性，以及获取、使用</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#2-3-%E9%87%8D%E8%A6%81%E7%9A%84%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F%E5%87%BD%E6%95%B0"><span class="toc-text">2.3 重要的生命周期函数</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#2-4-View%E7%9A%84%E8%87%AA%E5%88%B7%E6%96%B0"><span class="toc-text">2.4 View的自刷新</span></a></li></ol></li></ol></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#3-%E5%AE%9E%E8%B7%B5"><span class="toc-text">3. 实践</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#3-1%E5%A3%B0%E6%98%8E%E8%87%AA%E5%AE%9A%E4%B9%89%E5%B1%9E%E6%80%A7"><span class="toc-text">3.1声明自定义属性</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#3-2-%E8%87%AA%E5%AE%9A%E4%B9%89View%E7%B1%BBjava%E6%96%87%E4%BB%B6"><span class="toc-text">3.2 自定义View类java文件</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#3-3-%E5%B8%83%E5%B1%80%E6%96%87%E4%BB%B6%E4%BD%BF%E7%94%A8"><span class="toc-text">3.3 布局文件使用</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#3-4-Activity%E4%B8%AD%E4%BD%BF%E7%94%A8"><span class="toc-text">3.4 Activity中使用</span></a></li></ol></li></ol></li></ol></li></ol>
  </article>
  
  
  <article class="card card-content">
    <div class="recent-posts-card">
  <div class="recent-posts-header"><i class="iconfont icon-wenzhang_huaban" style="padding-right: 2px;"></i>最近文章</div>
  <div class="recent-posts-list">
    
      <div class="recent-posts-item">
        <div class="recent-posts-item-title">2021-03-02</div>
        <a href="/2021/03/02/Git commit规范/"><div class="recent-posts-item-content">Git commit规范</div></a>
      </div>
    
      <div class="recent-posts-item">
        <div class="recent-posts-item-title">2021-02-16</div>
        <a href="/2021/02/16/AutoJS一文精通AutoJS脚本教程详解/"><div class="recent-posts-item-content">AutoJS一文精通AutoJS脚本教程详解</div></a>
      </div>
    
      <div class="recent-posts-item">
        <div class="recent-posts-item-title">2021-01-21</div>
        <a href="/2021/01/21/架构师进阶：注解Annotation全解/"><div class="recent-posts-item-content">架构师进阶：注解Annotation全解</div></a>
      </div>
    
      <div class="recent-posts-item">
        <div class="recent-posts-item-title">2021-01-06</div>
        <a href="/2021/01/06/ConstraintLayout详解/"><div class="recent-posts-item-content">ConstraintLayout详解</div></a>
      </div>
    
  </div>
</div>
  </article>
  
  
</div>
          </aside>
        </div>
      </div>
    </div>
  </div>
  
  <footer class="footer">
  <div class="footer-container">
    <div>
      <div class="footer-dsc">
        <span>Copyright ©
          
          2020 -
          
          2021
        </span>
        &nbsp;
        <a href="/" class="footer-link">不负明天 </a>
      </div>
    </div>

    
    <div class="footer-dsc">
      
      Powered by
      <a href="https://hexo.io/" class="footer-link" target="_blank" rel="nofollow noopener noreferrer">&nbsp;Hexo </a>
      
      
      <span>&nbsp;|&nbsp;</span>
      
      
      Theme -
      <a href="https://github.com/theme-kaze" class="footer-link" target="_blank"
        rel="nofollow noopener noreferrer">&nbsp;Kaze</a>
      
    </div>
    
    
    
    
      <div class="footer-dsc">
        
        本站总访问量686<span id="busuanzi_value_site_pv"></span>次
        
        
        <span>&nbsp;|&nbsp;</span>
        
        
        本站总访客数219<span id="busuanzi_value_site_uv"></span>次
        
      </div>
      
    
</footer>
  <a role="button" id="scrollbutton" class="basebutton"  aria-label="回到顶部">
  <i class="iconfont icon-arrowleft button-icon"></i>
</a>
<a role="button" id="menubutton" class="basebutton">
  <i class="iconfont icon-menu button-icon"></i>
</a>
<a role="button" id="popbutton" class="basebutton" aria-label="控制中心">
  <i class="iconfont icon-expand button-icon"></i>
</a>
<a role="button" id="darkbutton" class="basebutton darkwidget" aria-label="夜色模式">
  <i class="iconfont icon-weather button-icon"></i>
</a>
<a role="button" id="searchbutton" class="basebutton searchwidget" aria-label="搜索">
  <i class="iconfont icon-search button-icon"></i>
</a>

  
  
  
  <script>
  var addImgLayout = function () {
    var img = document.querySelectorAll('.post-content img');
    var i;
    for (i = 0; i < img.length; i++) {
      var wrapper = document.createElement('a');
      wrapper.setAttribute('href', img[i].getAttribute('data-src'));
      wrapper.setAttribute('aria-label', 'illustration');
      wrapper.style.cssText = 'width: 100%; display: flex; justify-content: center;';
      if (img[i].alt) wrapper.dataset.caption = img[i].alt;
      wrapper.dataset.nolink = true;
      img[i].before(wrapper);
      wrapper.append(img[i]);
      var divWrap = document.createElement('div');
      divWrap.classList.add('gallery');
      wrapper.before(divWrap);
      divWrap.append(wrapper);
    }
    baguetteBox.run('.gallery');
  }
</script>
<script>loadScript("/js/lib/lightbox/baguetteBox.min.js", addImgLayout)</script>
  
  
  
<script src="/js/main.js"></script>

  
  <script>loadScript("/js/lib/busuanzi.min.js")</script>
  
  
  <script>
    var addLazyload = function () {
      var observer = lozad('.lozad', {
        load: function (el) {
          el.srcset = el.getAttribute('data-src');
        },
        loaded: function (el) {
          el.classList.add('loaded');
        }
      });
      observer.observe();
    }
  </script>
  <script>loadScript("/js/lib/lozad.min.js", addLazyload)</script>
  
  
</body>

</html>